#ifndef PLANNODE_H
#define PLANNODE_H

#include <string>
#include <vector>
#include <memory>
#include "sql/Stat.h"
#include "sql/statCreate.h"
#include <sstream>   
#include "sql/statUpdate.h"  

namespace ment{
    std::ostream& operator<<(std::ostream& os, const ColumnType& ct);
    enum PlanNodeType {
        kPlanSeqScan, kPlanFilter, kPlanProject,
        kPlanInsert, kPlanCreateTable, kPlanDelete, kPlanCreateSchema, kPlanUse,
        kPlanCreateIndex, kPlanUpdate
    };

    struct PlanNode {
        PlanNode(PlanNodeType type);
        virtual ~PlanNode() = default;
        virtual std::string toJSON() const = 0;
        PlanNodeType type;
    };

    struct SeqScanNode : PlanNode {
        std::string tableName;
        SeqScanNode(const std::string& table);
        std::string toJSON() const override;
    };

    struct FilterNode : PlanNode {
        Stat* condition;
        std::unique_ptr<PlanNode> child;
        FilterNode(Stat* cond, PlanNode* childNode);
        std::string toJSON() const override;
    };

    struct ProjectNode : PlanNode {
        std::vector<Stat*>* columns;
        std::unique_ptr<PlanNode> child;
        ProjectNode(std::vector<Stat*>* cols, PlanNode* childNode);
        std::string toJSON() const override;
    };

    struct InsertNode : PlanNode {
        std::string tableName;
        std::vector<Stat*>* values;
        InsertNode(const std::string& table, std::vector<Stat*>* vals);
        std::string toJSON() const override;
    };

    struct CreateTableNode : PlanNode {
        std::string tableName;
        std::vector<ColumnDefinition*>* columns;
        CreateTableNode(const std::string& table, std::vector<ColumnDefinition*>* cols);
        std::string toJSON() const override;
    };

    struct DeleteNode : PlanNode {
        std::string tableName;
        Stat* condition;
        DeleteNode(const std::string& table, Stat* cond);
        std::string toJSON() const override;
    };

    struct CreateSchemaNode : PlanNode {
        std::string schemaName;
        CreateSchemaNode(const std::string& name);
        std::string toJSON() const override;
    };

    struct UseNode : PlanNode {
        std::string schema;
        UseNode(const std::string& s) : PlanNode(kPlanUse), schema(s) {}
        std::string toJSON() const override {
            return "{\"op\":\"Use\",\"schema\":\"" + schema + "\"}";
        }
    };

    struct CreateIndexNode : PlanNode {
        std::string indexName;
        std::string tableName;
        std::string columnName;
        CreateIndexNode(const std::string& idx, const std::string& tbl, const std::string& col)
            : PlanNode(kPlanCreateIndex), indexName(idx), tableName(tbl), columnName(col) {
        }
        std::string toJSON() const override;
    };

    //9.12
    struct UpdateNode : PlanNode {
        std::string tableName;
        std::vector<ment::UpdateClause*>* updates;
        std::unique_ptr<PlanNode> child;

        UpdateNode(const std::string& tbl,
            std::vector<ment::UpdateClause*>* ups,
            PlanNode* scan);
        std::string toJSON() const override;
    };
} // namespace hsql
#endif